import 'dart:async';

import 'package:meta/meta.dart';
import 'package:mobx/mobx.dart';

part 'indicator.g.dart';

class IndicatorState<T> = _IndicatorState<T> with _$IndicatorState<T>;

enum Status { untapped, loading, error, completed }

enum LoadingType { none, initial, continues }

class ErrorDescription {
  String text;
}

abstract class _IndicatorState<T> with Store {
  @observable
  T data;
  @observable
  LoadingType loadingType;
  @observable
  ErrorDescription error;
  @observable
  Status status;
  Completer<T> _awaitCompleter;

  _IndicatorState(
      {T data,
      this.loadingType = LoadingType.initial,
      this.error,
      this.status = Status.untapped}) {
    this.data = data;
  }

//  @computed
//  T get data => _data;

  @action
  setStatus(
      {@required Status status,
      T data,
      ErrorDescription error,
      LoadingType loadingType}) {
    this.status = status;
    if (status == Status.completed) {
      this.data = data;
      this.loadingType = LoadingType.none;
      if (_awaitCompleter != null && !_awaitCompleter.isCompleted) {
        _awaitCompleter.complete(data);
      }
      return;
    }
    if (status == Status.error) {
      this.error = error;
      this.loadingType = LoadingType.none;
      if (_awaitCompleter != null) {
        _awaitCompleter.completeError(error);
      }
      return;
    }
    if (status == Status.loading) {
      this.loadingType = loadingType;
      if (data != null) {
        this.data = data;
      }
    }
  }

  FutureOr<T> awaitFor() {
    if (status == Status.completed) {
      return data;
    }
    if (status == Status.error) {
      return Future.error(error);
    }
    this._awaitCompleter = Completer<T>();
    return _awaitCompleter.future;
  }

  Future<void> fireFetch(Future<T> Function() action,
      {LoadingType loadingType = LoadingType.initial}) async {
    try {
      setStatus(status: Status.loading, loadingType: loadingType);
      final data = await action();
      setStatus(status: Status.completed, data: data);
    } catch (err) {
      setStatus(status: Status.error);
    }
  }
}
